import java.util.Enumeration;
import java.util.Vector;


public class No {

	private String simbolo;
	private Vector<No> filhos;
	private int quantidade;
	private No pai;
	boolean calcula;

	public No(String s) {
		simbolo = s;
		quantidade = 1;
		filhos = null;
		pai = null;
		calcula = true;
	}

	public Vector<No> getFilhos() {
		return this.filhos;
	}
	
	public void adiciona(String s) {
		
//		this.exibir("");
//		System.out.println("ADICIONAR " + s.charAt(s.length()-1) + " NO CONTEXTO " 
//		+ s.substring(0, s.length()-1));
		if (s.length()>1) {
			// se nao existir o contexto onde se est� trabalhando, reduz o contexto.

			if (!existeContexto(s)){
				this.adiciona(s.substring(1, s.length()));
			}
		}
		else {
			if (filhos == null)
				filhos = new Vector<No>();
			Enumeration<No> e = filhos.elements();
			boolean adicionar = true;
			while (e.hasMoreElements()) {
				No aux =  e.nextElement();
				if (s.startsWith(aux.simbolo)) {
					if (Codificador.getInstance().codifica) {
						calcula(aux.simbolo);
						Codificador.getInstance().codifica = false;
					}
					adicionar = false;
					aux.incrementa();
//					System.out.println("Incrementa : "+aux.simbolo);
					break;
				}
			}
			if (adicionar) {
				No aux = new No(s);
				aux.setPai(this);
				if (filhos.size()==0) {
					filhos.add(new No("esc"));
				}
				else {
					calcula("esc");
					No aux2 = localiza(filhos,"esc");
					aux2.incrementa();
				}
				//System.out.println("Consultando a probabilidade da tabela de ignorancia para o simbolo "+aux.simbolo);
				Codificador.getInstance().codificar(TabelaIgnorancia.getInstance().probSimbolo(aux.simbolo.charAt(0)));
				filhos.add(aux);
			}
		}
		calcula = true;
		//	System.out.println("\n\n\n");
	}

	public boolean equals(Object o) {
		No a = (No)o;
		return (simbolo.equals(a.simbolo));
	}

	public No localiza(Vector<No> filhos, String no) {
		Enumeration<No> e = filhos.elements();
		while (e.hasMoreElements()) {
			No aux =  e.nextElement();
			if (no.equals(aux.simbolo)) {
				return aux;
			}
		}
		return null;
	}

	public boolean existeContexto(String s) {
//		if (pai != null)
//		System.out.println(" pai = "+pai.simbolo+" simbolo = "+simbolo);
//		else
//		System.out.println(" pai = null  simbolo = "+simbolo);
//		System.out.println("Entrou aqui com a string : "+s);
		if ((filhos!=null)&&(filhos.size()>0)) {
			Enumeration<No> e = filhos.elements();
			while (e.hasMoreElements()) {
				No aux =  e.nextElement();
				if (s.startsWith(aux.simbolo)) {
					if ((aux.filhos == null)&&(s.length()>1)) {
//						System.out.println("Entrando aqui... sem filhos e com s = "+s);
						aux.filhos = new Vector<No>();
						No aux2 = new No(s.substring(1, 2));
						aux2.setPai(this);
						No aux3 = new No("esc");
						aux3.setPai(this);

						aux.filhos.add(aux2);
						aux.filhos.add(aux3);
						return false;
					}
					else if (s.length()<=1) {
						aux.incrementa();
						return true;
					}
					else {
						if ((s.length()>2)&&(aux.existeContexto(s.substring(1, s.length())))) {
							return true;
						}
						else {

							if (s.length()>2) {
//								System.out.println("ENTROU AQUI COM s MAIOR QUE 2!!!!!!");
								return false;
							}

							if (s.length()==2) {

								No aux5 = localiza(aux.filhos, s.substring(1, 2));
//								System.out.println("Procurando contexto "+s.substring(1, 2)+" no nó "+aux.simbolo);
								if (aux5!=null) {
									if (Codificador.getInstance().codifica) {
										aux.calcula(s.substring(1, 2));
										Codificador.getInstance().codifica = false;
									}
//									System.out.println("encontrou : "+aux5.simbolo+ " e vai incrementa-lo");
									aux5.incrementa();
									calcula = false;
								}
								else {
									aux.calcula("esc");
									No aux2 = new No(s.substring(1, 2));
									No aux3 = localiza(aux.filhos, "esc");
									aux3.incrementa();
									aux.filhos.add(aux2);
								}
								return false;
							}
							else
								return false;
						}
					}
				}
			}
			No aux = new No(s.substring(0, 1));
			aux.setPai(this);
			filhos.add(aux);
			return false;
		}
		else
			return false;
	}

	public void setPai(No n) {
		pai = n;
	}

	public void calcula(String s) {


		//	System.out.println("calcula probabilidade do simbolo: "+s);
		//	System.out.println("nos elementos : ");


		boolean achou = false;
		int frequenciaAcumulada=0;

		int[] dados = new int[3];

		dados[0] = dados[1] = dados[2] = 0;

		Enumeration<No> e = filhos.elements();


		//No[] lista = (No[])arvore.toArray();
		//Arrays.sort(lista);

		while (e.hasMoreElements()) {
			No aux =  e.nextElement();


			//System.out.println(aux.simbolo+" - "+aux.quantidade);


			if( ((aux.simbolo).equals(s)) && !achou ){
				achou = true;
				dados[0] = frequenciaAcumulada; 
				dados[1] = frequenciaAcumulada + aux.quantidade;
			}
			frequenciaAcumulada += aux.quantidade;
		}

		dados[2] = frequenciaAcumulada;

		//	System.out.println("DADOS: " + s + " LOW: " + dados[0] + " HIGH: " + dados[1] + " TOTAL: " + dados[2]);
		Codificador.getInstance().codificar(dados);

	}

	/*	public Vector<No> ordenar(Vector<No> lista) {		
		int i, j, menor;
		No tmp;

		for (i = 0 ; i < lista.size() ; i++) 	{
			menor = i;
			for (j = i + 1 ; j<lista.size() ; j++)	{
				if (lista.elementAt(i).simbolo.compareTo(lista.elementAt(j).simbolo) < 0)
					menor = j;
			}
			tmp = lista.elementAt(i);
			lista.setElementAt(lista.elementAt(menor), i);
			lista.setElementAt(tmp, menor);
	    }

		return lista; 
	}*/

	public void incrementa() {
		quantidade++;
	}


	public void exibir(String espaco) {
		int cont=0;
		System.out.println(espaco+simbolo+"-"+quantidade );

		if ((filhos!=null)&&(filhos.size()>0)) {
			Enumeration<No> e = filhos.elements();
			while (e.hasMoreElements()) {
				No aux =  e.nextElement();
				aux.exibir(espaco+"\t|");
				cont += aux.quantidade;
			}
			System.out.println(espaco + "\t|" + "TOTAL " + cont);
		}

	}
	
// refazer este m�todo!!	
	
	public No retornarContexto(String contextoReal) {
		
		System.out.println("contexto real : "+contextoReal);
		System.out.println("simbolo : "+this.simbolo);

		if( contextoReal.equals("") ) return this;
		
		if ( this.filhos == null )
			return this.pai;
		
		for(int i=0; i<this.filhos.size(); i++) {
			if((contextoReal.charAt(0) + "" ).equals (this.filhos.get(i).simbolo) ) {
				if(contextoReal.length() == 1) {
					//return this.filhos.get(i);
					return this;
				} else {
					return this.filhos.get(i).retornarContexto(contextoReal.substring(1));
				}
			}
		}
		
		return this;
		
//		if( contextoReal.equals("") ) return this;
//		
//		for(int i=0; i<this.filhos.size(); i++) {
//			if((contextoReal.charAt(0) + "" ).equals (this.filhos.get(i).simbolo) ) {
//				if(contextoReal.length() == 1) {
//					return this.filhos.get(i);
//				} else {
//					return this.filhos.get(i).retornarContexto(contextoReal.substring(1));
//				}
//			}
//		}
//		
//		return null;
	}

	public String getSimbolo() {
		return simbolo;
	}	


	public String retornaSimbolo(int valorAritmetido) {

		Enumeration<No> e = filhos.elements();
		int frequenciaAcumulada = 0;
		No aux=null;
		while (e.hasMoreElements()) {
			aux =  e.nextElement();
			frequenciaAcumulada += aux.quantidade;
			if (frequenciaAcumulada > valorAritmetido){
				return aux.getSimbolo();
			}
		}
		return "ERRO!";
	}

	public int [] retornaDadosAritmetico(String s) {

		boolean achou = false;
		int frequenciaAcumulada=0;

		int[] dados = new int[3];

		dados[0] = dados[1] = dados[2] = 0;

		Enumeration<No> e = filhos.elements();

		while (e.hasMoreElements()) {
			No aux =  e.nextElement();

			if( ((aux.simbolo).equals(s)) && !achou ){
				achou = true;
				dados[0] = frequenciaAcumulada; 
				dados[1] = frequenciaAcumulada + aux.quantidade;
			}
			frequenciaAcumulada += aux.quantidade;
		}

		dados[2] = frequenciaAcumulada;

		return dados;
	}

	public int totalDeSimbolosNoContexto() {
		Enumeration<No> e = this.filhos.elements();

		int retorno = 0;
		
		while (e.hasMoreElements()) {
			No aux =  e.nextElement();
			retorno += aux.quantidade;
		}
		return retorno;
	}


}